package com.walker.component.page.resolve.query;

import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.walker.component.page.resolve.exception.ParsingException;
import com.walker.component.page.resolve.query.annotation.Required;
import com.walker.component.utils.PatternTools;

/**
 * 通用查询解析器
 * 
 * @author 三丰
 * 
 * @param <T>
 */
public abstract class GeneralQueryer<T> implements Queryer {
	private int index = -1;
	private String cssQuery;
	Pattern grepPattern = null;
	protected int patternGroupIndex = 1;
	private String output = "text";

	public void setOutput(String output) {
		this.output = output.toLowerCase();
	}

	public void setGroupIndex(int patternGroupIndex) {
		this.patternGroupIndex = patternGroupIndex;
	}

	@Required
	public void setCssQuery(String xPathExpression) throws IllegalArgumentException {
		this.cssQuery = xPathExpression;
	}

	public void setIndex(int index) {
		this.index = index;
	}

	public void setGrep(String pattern) {
		this.grepPattern = Pattern.compile(pattern);
		if (this.grepPattern.matcher("").groupCount() < 1) {
			throw new IllegalArgumentException("The 'grep' property of a GeneralQueryer must have exactly 1 capturing group.");
		}
	}

	protected Object generate(Document document) {
		try {
			if ("".equals(this.cssQuery)) {
				return "";
			}
			Elements eles = document.select(this.cssQuery);
			StringBuilder sb = new StringBuilder();
			if ((this.index > -1) && (this.index < eles.size())) {
				sb.append(eleToString(eles.get(this.index)));
			} else {
				for (int i = 0; i < eles.size(); ++i) {
					Element ele = eles.get(i);
					sb.append(eleToString(ele));
				}
			}
			return sb.toString();
		} catch (Exception e) {
			throw new ParsingException("Error selecting  on " + document, e);
		}
	}

	protected Object generate(Element element) {
//		if (".".equals(this.cssQuery)) {
//			return eleToString(element);
//		}
//		Elements eles = element.select(this.cssQuery);
//		if (eles != null) {
//			return eleToString(eles);
//		}
//		return "";
		if ("".equals(this.cssQuery)) {
			return "";
		}
		Elements eles = element.select(this.cssQuery);
		StringBuilder sb = new StringBuilder();
		if ((this.index > -1) && (this.index < eles.size())) {
			sb.append(eleToString(eles.get(this.index)));
		}else{
			return eleToString(eles);
		}
		return sb.toString();
	}

	public String eleToString(Element ele) {
		if ("outerhtml".equals(this.output)) {
			return ele.outerHtml();
		}
		if ("owntext".equals(this.output)) {
			return ele.ownText();
		}
		if ("html".equals(this.output)) {
			return ele.html();
		}
		if (this.output.contains("attr")) {
			String attrValue = PatternTools.getGroupOne(output, "attr\\[(.*?)\\]");
			return ele.attr(attrValue);
		}
		return ele.text();
	}

	@SuppressWarnings("rawtypes")
	public String eleToString(Elements ele) {
		if ("outerhtml".equals(this.output)) {
			return ele.outerHtml();
		}
		if ("owntext".equals(this.output)) {
			StringBuffer sb = new StringBuffer();
			Iterator localIterator = ele.iterator();
			while (true) {
				Element el = (Element) localIterator.next();
				sb.append(el.ownText());
				if (!(localIterator.hasNext())) {
					return sb.toString();
				}
			}
		}
		if ("html".equals(this.output)) {
			return ele.html();
		}
		if (this.output.contains("attr")) {
			String attrValue = PatternTools.getGroupOne(output, "attr\\[(.*?)\\]");
			return ele.attr(attrValue);
		}
		return ele.text();
	}

	public T query(Document node) {
		Object obj = null;
		obj = generate(node);
		obj = grep(obj);
		return convert(obj);

	}

	public T query(Element ele) {
		Object obj = null;
		obj = generate(ele);
		obj = grep(obj);
		return convert(obj);
	}

	protected final Object grep(Object obj) {
		if (this.grepPattern != null) {
			Matcher m = this.grepPattern.matcher(obj.toString());
			if (m.find()) {
				return m.group(this.patternGroupIndex);
			}
			return "";
		}
		return obj;
	}

	public abstract T convert(Object obj);
}
